package hit.util;

import hit.core.Item;
import hit.core.Product;
import hit.core.Size;
import hit.core.Size.Unit;
import hit.core.StorageUnit;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.Barcode;
import com.itextpdf.text.pdf.BarcodeEAN;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * Uses the iText library to write Barcodes to PDF for printing
 * */
public class BarcodePrinter {

	/**
	 * Takes a list of {@link Item}s and creates a PDF to print barcodes for those items.
	 * @param items The list of items to print barcodes for.
	 * @param file The {@link File} to write to.
	 * @return The {@link File} for the newly created list of items.
	 * @throws IllegalArgumentException if the list is null, or the items are invalid.
	 */
	public static File Print(List<Item> items, File file) throws IllegalArgumentException {
		if (items == null) {
			throw new IllegalArgumentException("List of Items to print is null");
		}
		
		try {
			//open document resources
			Document document = new Document(PageSize.LETTER);
			PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
			document.open();
			
			//this table will contain all the barcodes
			PdfPTable table = new PdfPTable(6);
			table.setLockedWidth(true);
			table.setTotalWidth(580);
			
			//loop through the items and add them as barcodes
			int count = 0;
			for (Item item: items) {
				PdfPCell barcode = new PdfPCell(GetBarcodeImage(writer, item));
				barcode.setBorder(Rectangle.NO_BORDER);
				barcode.setPaddingTop(8f);
				table.addCell(barcode);
				
				count = (count + 1) % 6;
			}
			
			//pad out the leftover cells with empty cells
			for (; count < 6; count++) {
				PdfPCell empty = new PdfPCell();
				empty.setBorder(Rectangle.NO_BORDER);
				table.addCell(empty);
			}
			
			document.add(table);

			document.close();
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		return file;
	}
	
	/**
	 * Generates a PdfPTable that contains a description and a barcode
	 * @param writer the writer that will write the barcode
	 * @param item the Item to get a barcode for
	 * @return the barcode image
	 * */
	private static PdfPTable GetBarcodeImage(PdfWriter writer, Item item) 
											throws IllegalArgumentException {
		//first check that the item is valid
		if (!Item.IsValid(item))
			throw new IllegalArgumentException("Invalid Item to print");
		
		//create a table that contains a description and a barcode
		PdfPTable table = new PdfPTable(1);
		table.setLockedWidth(true);
		table.setTotalWidth(75);
		
		//add the description of the barcode
		Font font = new Font();
		font.setFamily("Helvetica");
		font.setSize(7);
		PdfPCell descriptionCell = new PdfPCell(
				new Phrase(item.GetProduct().GetDescription(), font));
		descriptionCell.setBorder(Rectangle.NO_BORDER);
		descriptionCell.setPaddingBottom(-2f);
		table.addCell(descriptionCell);
		
		//add the creation date and expiration date
		DateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
		StringBuilder builder = new StringBuilder(dateFormat.format(item.GetEntry()));
		if (item.GetExpiration() != null)
			builder.append(" exp " + dateFormat.format(item.GetExpiration()));
		PdfPCell dateCell = new PdfPCell(new Phrase(builder.toString(), font));
		dateCell.setBorder(Rectangle.NO_BORDER);
		table.addCell(dateCell);
		
		//add the barcode to the pdf
		BarcodeEAN barcode = new BarcodeEAN(); 
		barcode.setCodeType(Barcode.UPCA); 
		barcode.setCode(item.GetBarcode().GetValue());
		Image img = barcode.createImageWithBarcode(
		  writer.getDirectContent(), null, null); 
		PdfPCell barcodeCell = new PdfPCell(img);
		barcodeCell.setBorder(Rectangle.NO_BORDER);
		table.addCell(barcodeCell);
		
		return table;
	}
	
	public static void main(String[] args) {
		try {
			Product product = new Product();
			product.SetBarcode(new hit.core.Barcode("885370395143"));
			product.SetDescription("test");
			product.SetCreation(new Date());
			product.SetShelfLife(3);
			product.SetSize(new Size(4.0, Unit.COUNT));
			product.SetThreeMonth(3);
			
			Item item1 = product.CreateItem(new StorageUnit("Test Unit"), new Date());
			item1.SetEntry(new Date());
			Item item2 = product.CreateItem(new StorageUnit("Unit 2"), new Date());
			item2.SetEntry(new Date());
			
			List<Item> items = new ArrayList<Item>();
			items.add(item1);
			items.add(item2);
			
			for (int i = 0; i < 13; i++) {
				items.add(product.CreateItem(new StorageUnit("Unit"), new Date()));
			}
			
			java.awt.Desktop.getDesktop().open(
					BarcodePrinter.Print(items, new File("test.pdf")));
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
